# -*- coding:utf-8 -*-
import os,sys
import re
import traceback
import time
sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), os.pardir, os.pardir))
import supeanut_config
from CommonLib.mylog import mylog
from CommonLib.IndexCal.IndexCal import IndexCal


'''
作者：supeanut
创建时间：2016-xx-xx xx:xx:xx
功能描述：
	supeanut技术指标公式识别
相关配置：
	supeanut_config.XXX
历史改动：
	2016-xx-xx: xxxxxx
'''
class SenRecog:
	# init what
	def __init__(self):
		self.az = set(['_','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'])
		self.num = set(['0','1','2','3','4','5','6','7','8','9','.'])
		self.op = set(['+', '-', '*', '/', '>', '<', '!', '=', 'o', 'r', 'a', 'n', 'd', 'n', 'o', 't'])
		# 支持的运算符
		self.op_list = ['+', '-', '*', '/', '>', '<', '<=', '>=', '!=','=', 'and', 'not', 'or']
		# 识别到当前字符运算符即可确定op
		self.noconflict_op = set(['+', '-', '*', '/', '<=', '>=', '!=','=', 'and', 'not', 'or'])
		self.op_priority = {'(':1,
							'not':2,
							'*':3, '/':3,
							'+':4, '-':4,
							'>':6, '<':6, '>=':6, '<=':6,
							'=':7, '!=':7,
							'and':11,
							'or':12}
		indexcal_obj = IndexCal()
		self.index_list = indexcal_obj.get_all_hqdateindex_name()
		self.child_index_list = indexcal_obj.get_all_index_child()

	# input a unicode str
	def clean_sentence(self, ori_sen):
		# clean space
		ori_sen = ori_sen.replace(u' ', u'')
		# clean \r \n
		ori_sen = ori_sen.replace(u'\r', u'')
		ori_sen = ori_sen.replace(u'\n', u'')
		# lower a-z
		ori_sen = ori_sen.lower()
		return ori_sen

	# find index str
	# instead the index by INDEX
	def extract_index(self, clean_str):
		az_str = ""
		status = 'normal'
		index_str = ""
		index_str_list = []
		cur_pos = -1
		instead_str = ""
		index_s = 0
		index_e = -1
		for cur in clean_str:
			cur_pos += 1
			if status == 'index' and cur <> ')':
				index_str += cur
				continue
			if status == 'index' and cur == ')':
				instead_str += clean_str[index_e+1: index_s] + 'I'
				index_e = cur_pos
				index_str += cur
				index_str_list.append(index_str)
				status = 'normal'
				index_str = ""
				continue
			if cur in self.az:
				az_str += cur
			else:
				if az_str in self.index_list or az_str in self.child_index_list:
					status = 'index'
					index_s = cur_pos - len(az_str)
					index_str += az_str + cur
				az_str = ""
		instead_str += clean_str[index_e+1:]
		return index_str_list, instead_str

	# index str transform to [child, index, [params], day_offset]
	# invalid form is checked
	def recog_index(self, index_str_list):
		index_form_list = []
		for index_str in index_str_list:
			index_form = []
			# @ pos
			at_pos = index_str.find('@')
			if at_pos < 0:
				index_form.append('')
			else:
				child_index = index_str[:at_pos]
				if child_index not in self.child_index_list:
					return False
				index_form.append(child_index)
			# ( pos
			zuokuohao_pos = index_str.find('(')
			if zuokuohao_pos < 0:
				return False
			try:
				index = index_str[at_pos+1:zuokuohao_pos]
				if index not in self.index_list:
					return False
				index_form.append(index)
				# ) pos
				youkuohao_pos = index_str.find(')')
				param_dayoff_str = index_str[zuokuohao_pos+1:youkuohao_pos]
				splits = param_dayoff_str.split(',')
				if len(splits) < 1:
					return False
				dayoff_str = splits[-1]
				params_str = splits[:-1]
				params = []
				for param in params_str:
					params.append(float(param))
				index_form.append(params)
				index_form.append(int(dayoff_str))
			except:
				return False
			index_form_list.append(index_form)
		return index_form_list

	# 验证param和dayoffset的合法性
	def check_valid(self, instead_str, index_form_list):
		for index_form in index_form_list:
			index = index_form[1]
			params = index_form[2]
			dayoff_set = index_form[3]
			# check valid
			# ...
		return True

	# 获取num_list
	# 用'N'代替num
	def extract_num(self, instead_str):
		instead_str_len = len(instead_str)
		num_str = ""
		number_list = []
		cur_pos = -1
		e_pos = 0
		s_pos = 0
		instead_str2 = ""
		only_end_num = False
		for cur in instead_str:
			cur_pos += 1
			if cur in self.num:
				if num_str == "":
					s_pos = cur_pos
				num_str += cur
				if cur_pos == (instead_str_len - 1):
					only_end_num = True
					instead_str2 += instead_str[e_pos:s_pos] +'N'
					e_pos = cur_pos
					try:
						if num_str.find('.') < 0:
							number = int(num_str)
						else:
							number = float(num_str)
						number_list.append(number)
						num_str = ""
					except:
						return False
				continue
			else:
				if num_str == "":
					continue
				instead_str2 += instead_str[e_pos: s_pos] + 'N'
				e_pos = cur_pos
				try:
					if num_str.find('.') < 0:
						number = int(num_str)
					else:
						number = float(num_str)
					number_list.append(number)
					num_str = ""
				except:
					return False
		if not only_end_num:
			instead_str2 += instead_str[e_pos:]
		return number_list, instead_str2
	
	# 获取op列表
	# 用P代替op，返回str
	def extract_op(self, instead_str):
		op_list = []
		op_str = ""
		instead_str2 = ""
		cur_pos = -1
		op_s = 0
		op_e = -1
		for cur in instead_str:
			cur_pos += 1
			if cur in self.op:
				op_str += cur
				if op_str in self.noconflict_op:
					op_s = cur_pos - (len(op_str) - 1)
					instead_str2 += instead_str[op_e+1: op_s] + 'P'
					op_e = cur_pos
					op_list.append(op_str)
					op_str = ""
				continue
			else:
				if op_str <> "":
					if op_str in self.op_list:
						# 仅匹配出 <, >
						op_list.append(op_str)
						op_s = cur_pos - 1 - (len(op_str) - 1)
						instead_str2 += instead_str[op_e+1: op_s] + 'P'
						op_e = cur_pos - 1
					else:
						return False
				op_str = ""
		instead_str2 += instead_str[op_e+1:]
		return op_list, instead_str2
				
				
	def calculate_value_by_eval(self, instead_str, indexs_value):
		for index_value in indexs_value:
			instead_str = instead_str.replace('I',str(index_value),1)
		result = eval(instead_str)
		if isinstance(result,bool):
			if result is True:
				result = 1
			else:
				result = 0
		return result
	
	# IPNPIP(IP((IPN)P(NPI)))
	# I: [1,2,3,4]
	# N: [12.3, 0, 12]
	# P: [u'-', u'+', u'-', u'/', u'>=', u'and', u'+']
	# 计算最终结果
	# python 栈： list.pop() list.append()
	# self.op_priority = {'+':2}
	def calculate_value(self, instead_str, num_list, indexs_value, op_list):
		print instead_str
		print num_list
		print op_list
		stack = []
		op_stack = []
		N_index = 0
		I_index = 0
		P_index = 0
		for cur in instead_str:
			if cur == 'N':
				stack.append(num_list[N_index])
				N_index += 1
				continue
			if cur == 'I':
				stack.append(indexs_value[I_index])
				I_index += 1
				continue
			if cur == '(':
				stack.append('(')
				op_stack.append('(')
				continue
			

			
			


if __name__ == '__main__':
	obj = SenRecog()
	test_str = u"ma(3) - 12.3 +  ma(3,-2) - ( d@Kdj(33,9,9,0) /((macd@MAcd(1,2,3)>=0)and (12 + close(0)))) \r\n"
	#test_str = u"12.3 +  ma(3,-2) - ( d@Kdj(33,9,9,0) /((macd@MAcd(1,2,3)>=0)and (12 + close(0)))) \r\n"
	#test_str = u">2=3!==6and6"
	test_str = u"dateyear(0)"
	test_str2 = "3"
	test_str3 = "ma(3) - 12.3"
	test_str4 = "ma(3)"
	test_str5 = "ma(3)"
	print 'clean str...'
	clean_str = obj.clean_sentence(test_str)
	print clean_str
	print 'index extract...'
	index_str_list, instead_str = obj.extract_index(clean_str)
	print instead_str
	print index_str_list
	'''
	# eval方式
	print obj.calculate_value_by_eval(instead_str, [1,2,3,4,5])
	print obj.calculate_value_by_eval(instead_str, [1,2,3,4,5])
	print 'large scale tie start ...'
	for i in range(1):
		print obj.calculate_value_by_eval(instead_str, [1, 33, 5.3, 6.123, 231])
	print 'large scale tie end ...'	
	'''
	# 传统方式
	print 'num extract...'
	num_list, instead_str = obj.extract_num(instead_str)
	print instead_str
	print num_list
	print 'op extract...'
	op_list, instead_str = obj.extract_op(instead_str)
	print instead_str
	print op_list
	print 'recog index...'
	index_form_list = obj.recog_index(index_str_list)
	print index_form_list
	print 'check valid...'
	print obj.check_valid(instead_str, index_form_list)
	print 'calculate value...'
	print obj.calculate_value(instead_str, num_list, [1, 3, 5, 6, 1], op_list)
	exit(0)
	print 'large scale tie start ...'
	for i in range(1000):
		obj.calculate_value(instead_str, num_list, [1, 33, 5.3, 6.123, 231], op_list)
	print 'large scale tie end ...'	
